/* View as Hex routines */

#include "global.h"
#include "vu-res.h"			/* general resource file ids */
#include "hex.h"				/* hex resource file ids */

#define HEX_PARA_SIZE			8

/* Hex File data */
typedef struct {
	VOID *	pwfcb ;				/* pointer to the write file control block */
	UINT		lines ;				/* lines on screen */
	LONG		curr_pos ;			/* current position in the file */
	LONG		last_pos ;			/* offset of the last top position in the file */
	UINT		total_lines ;		/* total lines of data (8 bytes to a line) */
	BOOL		redraw ;				/* redraw the screen? */
	INT		file_op ;			/* operation on the file */
} HEX_DATA ;

LOCAL_D HEX_DATA *		pdata = NULL ;

LOCAL_C VOID GetNewPos(VOID) {
	H_DI_NUMBER		num ;

	num.value = &pdata->curr_pos ;
	num.high = pdata->last_pos ;
	num.low = 0L ;

	Res_OpenDialog(RS_HEX_GOTO_BYTE) ;
	Res_AddDialogItem(H_DIALOG_NUMBER, RS_HEX_BYTE, &num) ;

	if ( Res_RunDialog() ) {
		/* Round down to an allowed value */
		pdata->curr_pos -= (pdata->curr_pos % HEX_PARA_SIZE) ;
		f_seek(file.pfcb, P_FABS, &pdata->curr_pos) ;
	}
}

LOCAL_C VOID LoadHex(VOID) {
	INT		iLp ;
	INT		jLp ;
	INT		lines_to_read ;
	INT		bytes_read = HEX_PARA_SIZE ;
	UBYTE		buf[HEX_PARA_SIZE] ;
	TEXT		str[3 * HEX_PARA_SIZE + 10] ;
	BOOL		sk_cur = FALSE ;
	BOOL		sk_abs = FALSE ;

	switch ( pdata->file_op ) {
		case FO_GOTO :
			GetNewPos() ;
			break ;

		case FO_TOP :
			pdata->curr_pos = 0L ;
			sk_abs = TRUE ;
			break ;
			
		case FO_BOTTOM :
			pdata->curr_pos = pdata->last_pos ;
			sk_abs = TRUE ;
			break ;

		case FO_UP :
			pdata->curr_pos = -MIN(pdata->curr_pos, HEX_PARA_SIZE) ;
			sk_cur = TRUE ;
			break ;

		case FO_DOWN :
			if ( pdata->last_pos >= pdata->curr_pos ) {
				pdata->curr_pos = MIN(pdata->last_pos - pdata->curr_pos, HEX_PARA_SIZE) ;
				sk_cur = TRUE ;
			}
			break ;

		case FO_PAGE_UP :
			pdata->curr_pos = -MIN(pdata->curr_pos, pdata->lines * HEX_PARA_SIZE) ;
			sk_cur = TRUE ;
			break ;

		case FO_PAGE_DOWN :
			if ( pdata->last_pos >= pdata->curr_pos ) {
				pdata->curr_pos = MIN(pdata->last_pos - pdata->curr_pos, pdata->lines * HEX_PARA_SIZE) ;
				sk_cur = TRUE ;
			}
			break ;

		default :
			return ;
	}

	if ( sk_abs ) {
		sk_abs = FALSE ;
		f_seek(file.pfcb, P_FABS, &pdata->curr_pos) ;
	}

	if ( sk_cur ) {
		sk_cur = FALSE ;
		f_seek(file.pfcb, P_FCUR, &pdata->curr_pos) ;
	}

	lines_to_read = (INT) MIN(pdata->total_lines, pdata->lines) ;

	f_seek(file.pfcb, P_FABS, &pdata->curr_pos) ;

	/* Read characters into buffer */
	for (	iLp = 0 ; bytes_read == HEX_PARA_SIZE && iLp < lines_to_read ; iLp++ ) {

		bytes_read = f_read(file.pfcb, buf, HEX_PARA_SIZE) ;

		/* In Hex */
		for (	str[0] = '\x0', jLp = 0 ; jLp < HEX_PARA_SIZE ; jLp++ ) {
			if (jLp < bytes_read)
				p_atos(&str[p_slen(str)], "%02x", (UINT) buf[jLp]) ;
			else
				p_scat(str, "  ") ;
			if ( jLp == HEX_PARA_SIZE / 2 - 1 )
				p_scat(str, " ") ;
		}

		/* In ASCII */
		for (	p_scat(str, "  "), jLp = 0 ; jLp < HEX_PARA_SIZE ; jLp++ ) {
			if (jLp < bytes_read) {
				if ( buf[jLp] > 32 && buf[jLp] < 127 )
					p_atos(&str[p_slen(str)], "%c", (UINT) buf[jLp]) ;
				else
					p_scat(str, ".") ;
			}
			else
				p_scat(str, " ") ;
			if ( jLp == HEX_PARA_SIZE / 2 - 1 )
				p_scat(str, " ") ;
		}

		PrintString(str, iLp) ;
	}

	f_seek(file.pfcb, P_FABS, &pdata->curr_pos) ;
}

LOCAL_C VOID SaveHex(VOID) {
	H_DI_NUMBER		num1 ;
	H_DI_NUMBER		num2 ;
	H_DI_FSEL		fsel = { NULL, H_FILE_NEW_EDITOR | H_FILE_FORCE_NXIST } ;
	LONG				bytes_to_write ;
	LONG				iLp ;
	TEXT				filename[P_FNAMESIZE] = "" ;
	BYTE				buf ;

	num1.value = &pdata->curr_pos ;
	num1.high = file.size ;
	num1.low = 0L ;

	bytes_to_write = MIN(HEX_PARA_SIZE * pdata->lines, file.size - pdata->curr_pos) ;
	num2.value = &bytes_to_write ;
	num2.high = file.size ;
	num2.low = 0L ;

	fsel.fname = filename ;

	Res_OpenDialog(RS_HEX_SAVE_SPEC) ;
	Res_AddDialogItem(H_DIALOG_FSEL, RS_SAVE_AS, &fsel) ;
	Res_AddDialogItem(H_DIALOG_NUMBER, RS_HEX_FIRST_BYTE, &num1) ;
	Res_AddDialogItem(H_DIALOG_NUMBER, RS_HEX_NUM_BYTES, &num2) ;
	if ( !Res_RunDialog() )
		return ;

	bytes_to_write = MIN(bytes_to_write, file.size - pdata->curr_pos) ;
	filename[filename[0] + 1] = '\x0' ;

	f_seek(file.pfcb, P_FABS, &pdata->curr_pos) ;

	f_open(&pdata->pwfcb, &filename[1], P_FREPLACE | P_FUPDATE) ;
	
	for ( iLp = 0L ; iLp < bytes_to_write ; iLp++ ) {
		f_read(file.pfcb, &buf, 1) ;
		f_write(pdata->pwfcb, &buf, 1) ;
	}
	p_close(pdata->pwfcb) ;
	pdata->pwfcb = NULL ;
	
	pdata->curr_pos = MIN(pdata->curr_pos - (pdata->curr_pos % HEX_PARA_SIZE), pdata->last_pos) ;
	f_seek(file.pfcb, P_FABS, &pdata->curr_pos) ;
}

LOCAL_D H_MENU_DATA		Hexmdata[] = {	"File", 3,
													"Hex", 1,
													NULL } ;

LOCAL_D TEXT *				Hexcmds[] = {	"oOpen",
													"sSave Current Page",
													"xExit",

													"jJump to byte",
													NULL } ;

#pragma save, ENTER_CALL

LOCAL_C INT HexCleanUp(VOID) {
	if ( pdata != NULL ) {
		if ( pdata->pwfcb != NULL ) {
			p_close(pdata->pwfcb) ;
			pdata->pwfcb = NULL ;
		}

		p_free(pdata) ;
		pdata = NULL ;
	}

	return(E_APP_NONE) ;
}

LOCAL_C INT HexInit(VOID) {
	pdata = f_alloc(sizeof(HEX_DATA)) ;

	f_open(&file.pfcb, file.name, P_FSTREAM | P_FOPEN | P_FRANDOM | P_FSHARE) ;
	pdata->pwfcb = NULL ;
	pdata->lines = metrics.disp_size.y / (metrics.text_height + TEXT_LEADING) ;
	pdata->redraw = FALSE ;
	pdata->file_op = FO_TOP ;
	pdata->curr_pos = 0 ;

	pdata->total_lines = (UINT) (file.size / HEX_PARA_SIZE) ;
	if ( file.size % HEX_PARA_SIZE )
		pdata->total_lines++ ;

	if ( pdata->total_lines <= pdata->lines )
		pdata->last_pos = 0 ;
	else
		pdata->last_pos = (pdata->total_lines - pdata->lines) * HEX_PARA_SIZE ;

	SetMainWindowExtent(	0,
								0,
								metrics.disp_size.x,
								MIN(pdata->lines, pdata->total_lines) * (metrics.text_height + TEXT_LEADING) ) ;

	return(E_APP_NONE) ;
}

LOCAL_C INT HexMenuExec(INT index) {
	switch ( index ) {
		case 0 :
			MainDialog() ;
			break ;

		case 1 :
			SaveHex() ;
			break ;

		case 2 :
			Quit() ;
			break ;

		case 3 :
			pdata->file_op = FO_GOTO ;
			break ;

		default :
			return(E_APP_NONE) ;
	}
	return(E_APP_NONE) ;
}

LOCAL_C INT HexUpdate(VOID) {
	TEXT						buf[128] ;
	SCROLL_BAR_STATE		bary ;

	if (	pdata->redraw ||
			pdata->file_op != FO_NONE ) {

		if ( pdata->file_op != FO_NONE )
			LoadHex() ;

		/* Filename : xx/xx xx%        arrows */
		p_atos(	buf, "%s : %ld/%lu",
					file.name,
					pdata->curr_pos,
					file.size ) ;
		UpdateStatus(buf) ;

		bary.start = (ULONG) pdata->curr_pos ;
		bary.size = (ULONG) (pdata->lines * HEX_PARA_SIZE) ;
		bary.total = (ULONG) file.size ;
		UpdateYScroll(&bary) ;

		pdata->redraw = FALSE ;
		pdata->file_op = FO_NONE ;
	}
	return(E_APP_NONE) ;
}

LOCAL_C INT HexPlainKey(WMSG_KEY *pkey) {
	switch (pkey->keycode) {

		case W_KEY_DOWN :
			if ( pdata->curr_pos < pdata->last_pos )
				pdata->file_op = pkey->modifiers & W_CTRL_MODIFIER ? FO_BOTTOM : FO_DOWN ;
			break ;

		case W_KEY_UP :
			if ( pdata->curr_pos > 0 )
				pdata->file_op = pkey->modifiers & W_CTRL_MODIFIER ? FO_TOP : FO_UP ;
			break ;

		case W_KEY_PAGE_DOWN :
			pdata->file_op = pdata->curr_pos < pdata->last_pos ? FO_PAGE_DOWN : FO_NONE ;
			break ;

		case W_KEY_PAGE_UP :
			pdata->file_op = pdata->curr_pos > 0 ? FO_PAGE_UP : FO_NONE ;
			break ;

		case 'g' :
		case 'j' :
		case 'G' :
		case 'J' :
			/* Goto specified byte */
			pdata->file_op = FO_GOTO ;
			break ;

		case 'w' :
		case 's' :
		case 'W' :
		case 'S' :
			SaveHex() ;
			break ;
		default :
			break ;
	}
	return(E_APP_NONE) ;
}

#pragma restore

GLDEF_C VOID HexDefns(DEFNS *pdefns) {
	pdefns->pfRecognize =	NULL ;
	pdefns->pcmds =			Hexcmds ;
	pdefns->pmdata =			Hexmdata ;
	pdefns->nHelp =			RH_HEX ;
	pdefns->flags =			SCROLL_Y | STATUS_BAR ;
	pdefns->style =			G_STY_MONO ;

	pdefns->pfCleanUp =		HexCleanUp ;
	pdefns->pfInit =			HexInit ;
	pdefns->pfFileInfo =		NULL ;
	pdefns->pfUpdate =		HexUpdate ;
	pdefns->pfMenuExec =		HexMenuExec ;
	pdefns->pfPlainKey =		HexPlainKey ;
}
